package com.tarena.lbs.article.web.repository;

import com.alibaba.fastjson2.JSON;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.pojo.content.entity.ActionSearchEntity;
import com.tarena.lbs.pojo.content.entity.ArticleSearchEntity;
import com.tarena.lbs.pojo.content.query.ArticleQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.collapse.CollapseBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Repository
@Slf4j
public class ArticleRepository {
    //有可能 依赖 spring-boot-starter-data-elasticsearch 提供了客户端的自动配置
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private ArticleESRepository articleESRepository;
    public List<ArticleSearchEntity> searchArtitles(ArticleQuery query) throws IOException {
        //当前仓储层功能 根据articleQuery 的查询条件 非空 拼接 searchRequest请求参数 做分页 查询
        //1.创建查询搜索的请求对象
        SearchRequest request=new SearchRequest("lbs_article");
        //2.封装请求参数
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        //封装该有的所有查询条件
        sourceCreate(sourceBuilder,query);
        request.source(sourceBuilder);
        log.info("本次文章分页查询条件:{}",sourceBuilder.query());
        //3.执行请求 查询对应数据
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHit[] hits = response.getHits().getHits();
        //4.准备一个返回list 封装填补元素
        List<ArticleSearchEntity> entities=new ArrayList<>();
        if (hits!=null&&hits.length>0){
            for (SearchHit hit : hits) {
                ArticleSearchEntity entity = JSON.parseObject(hit.getSourceAsString(), ArticleSearchEntity.class);
                entities.add(entity);
            }
        }
        return entities;
    }

    private void sourceCreate(SearchSourceBuilder sourceBuilder, ArticleQuery aquery) {
        //所有的非空 属性 对应封装成业务需要的查询条件
        //除了articleTitle标题 非空使用 matchQuery updateTime 非空 做rangeQuery latitude longitude非空做geoDistanceQuery 其他非空都是termQuery
        //总体查询条件并不是matchQuery rangeQuery termQuery geoDistanceQuery 而是一个复合查询条件bool
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        //根据非空属性query拼接不同字条件
        //1.文章标题非空 匹配字符串相似度
        if (StringUtils.isNotEmpty(aquery.getArticleTitle())){
            query.must(QueryBuilders.matchQuery("articleTitle",aquery.getArticleTitle()));
        }
        //2.分类id非空 判断term相等
        if (aquery.getArticleCategoryId()!=null){
            query.must(QueryBuilders.termQuery("articleCategoryId",aquery.getArticleCategoryId()));
        }
        //3.标签非空 判断term相等
        if (StringUtils.isNotEmpty(aquery.getArticleLabel())){
            query.must(QueryBuilders.termQuery("articleLabel",aquery.getArticleLabel()));
        }
        //4.文章状态 list 但是参数只可能有一个元素 term
        if (CollectionUtils.isNotEmpty(aquery.getArticleStatus())&&aquery.getArticleStatus().size()==1){
            query.must(QueryBuilders.termQuery("articleStatus",aquery.getArticleStatus().get(0)));
        }
        //5.source非空 term 只有后台才关心source 前台手机小程序 不关心source
        if (aquery.getSource()!=null){
            query.must(QueryBuilders.termQuery("source",aquery.getSource()));
        }
        //6.关联的活动id非空 term
        if (aquery.getActivityId()!=null){
            query.must(QueryBuilders.termQuery("activityId",aquery.getActivityId()));
        }
        //7.手机会定位 纬度 经度 latitude longitude 如果这个数据都不空 就做geoDistanceQuery 100公里范围
        if (StringUtils.isNotEmpty(aquery.getLatitude())&&StringUtils.isNotEmpty(aquery.getLongitude())){
            //中心点 构造方法需要double参数 将字符串转化
            GeoPoint center=new GeoPoint(
                    Double.valueOf(aquery.getLatitude()),
                    Double.valueOf(aquery.getLongitude()));
            GeoDistanceQueryBuilder disQuery = QueryBuilders.geoDistanceQuery("location").point(center).distance("100", DistanceUnit.KILOMETERS);
            query.must(disQuery);
        }
        //8 start end不为空 rangeQuery查询 文章创建时间
        if (aquery.getUpdateStartTime()!=null&&aquery.getUpdateEndTime()!=null){
            query.must(QueryBuilders.rangeQuery("updateTime").
                    gte(aquery.getUpdateStartTime()).
                    lte(aquery.getUpdateEndTime()));
        }
        //9.用户id非空 term 查询该作者的所有文章
        if (aquery.getUserId()!=null){
            query.must(QueryBuilders.termQuery("userId",aquery.getUserId()));
        }
        //将bool条件交给sourceBuilder
        sourceBuilder.query(query);
    }

    //DAY11-PM 01 、查询手机定位范围内的文章标签集合
    public List<ArticleSearchEntity> getArticleLabelsOnLocation(ArticleQuery query) throws IOException {
        //1.准备搜索请求对象
        SearchRequest request=new SearchRequest("lbs_article");
        //2.准备查询参数 查询条件根据手机定位 100公里范围的所有文章 对label做去重
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        //2.1拼接一个geoDistance
        GeoPoint center=new GeoPoint(Double.valueOf(query.getLatitude()),Double.valueOf(query.getLongitude()));
        GeoDistanceQueryBuilder geoQuery = QueryBuilders.geoDistanceQuery("location").point(center).distance(100D, DistanceUnit.KILOMETERS);
        sourceBuilder.query(geoQuery);
        //2.2基于articleLabel做去重处理
        sourceBuilder.collapse(new CollapseBuilder("articleLabel"));
        request.source(sourceBuilder);
        //3.发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //4.解析响应 list
        SearchHit[] hits = response.getHits().getHits();
        if (hits!=null&&hits.length>0){
            //将SearchHit数组转化成List<ArticleSearchEntity>
            return Arrays.stream(hits).map(doc->{
                String json = doc.getSourceAsString();
                return JSON.parseObject(json,ArticleSearchEntity.class);
            }).collect(Collectors.toList());
        }else{
            return null;
        }
    }


    public List<ArticleSearchEntity> getArticles(Integer action, Integer actionUserId) throws BusinessException {
        //1.到lbs_action 查询 条件 既满足actionUserId 又满足action 这个查询的结果有非常多文章 重复的id 需要去重
        SearchRequest actionRequest=new SearchRequest("lbs_action");
        SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
        //1.1 既满足 又满足 是布尔 两个must
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        TermQueryBuilder query1 = QueryBuilders.termQuery("action", action);
        TermQueryBuilder query2 = QueryBuilders.termQuery("actionUserId", actionUserId);
        query.must(query1).must(query2);
        sourceBuilder.query(query);
        //1.2 将查询到的所有用户行为的文章id做去重
        sourceBuilder.collapse(new CollapseBuilder("articleId"));
        actionRequest.source(sourceBuilder);
        //1.3 查询 解析response
        List<ActionSearchEntity> actionEntities=null;
        try {
            SearchHit[] hits = client.search(actionRequest, RequestOptions.DEFAULT).getHits().getHits();
            if (hits!=null&&hits.length>0){
                actionEntities = Arrays.stream(hits).map(doc->{
                    String json = doc.getSourceAsString();
                    return JSON.parseObject(json,ActionSearchEntity.class);
                }).collect(Collectors.toList());
            }
        } catch (IOException e) {
            log.error("查询出现异常",e);
            throw new BusinessException("-2","查询出现异常");
        }
        //2. 拿到一堆文章id
        List<ArticleSearchEntity> articleEntities=null;
        if (CollectionUtils.isNotEmpty(actionEntities)){
            articleEntities=new ArrayList<>();
            //3.利用文章id查询文章详情 批量查询
            for (ActionSearchEntity actionEntity : actionEntities) {
                Integer articleId=actionEntity.getArticleId();
                articleEntities.add(articleESRepository.findById(articleId).get());
            }
        }
        return articleEntities;
    }
}